/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "DecompressorBmp.h"
#include <Gluck.h>
#include <Endianess.h>

namespace liba
{
namespace pictureformat
{

static int get_remained_bytes(int sx, int bytes_per_pixel)
{
	return (4 - (sx*bytes_per_pixel) % 4) % 4;
}

#pragma pack(push, 1)
	struct BITMAPFILEHEADER
	{
		unsigned short	bfType;
		unsigned 	bfSize;
		unsigned short	bfReserved1;
		unsigned short	bfReserved2;
		unsigned	bfOffBits;
	};

	struct BITMAPINFOHEADER
	{
		unsigned	biSize;
		unsigned	biWidth;
		unsigned	biHeight;
		unsigned short	biPlanes;
		unsigned short	biBitCount;
		unsigned	biCompression;
		unsigned	biSizeImage;
		unsigned	biXPelsPerMeter;
		unsigned	biYPelsPerMeter;
		unsigned	biClrUsed;
		unsigned	biClrImportant;
	};
#pragma pack(pop)

DecompressorBmp::DecompressorBmp(const void * data, int size):
	current_data( 0 ),
	frame_count( 0 ),
	bytes_per_pixel( 0 )
{
	const char * current_pos = static_cast<const char *>( data );
	int remained_size = size;
	for( ; frame_count < MAX_MIPMAP_LEVEL; ++frame_count )
	{
		if( remained_size < sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))
			return;
		const BITMAPFILEHEADER * file_header = reinterpret_cast<const BITMAPFILEHEADER*>(current_pos);
		current_pos += sizeof(BITMAPFILEHEADER);
		remained_size -= sizeof(BITMAPFILEHEADER);
		const BITMAPINFOHEADER * bitmap_info_header = reinterpret_cast<const BITMAPINFOHEADER*>(current_pos);
		current_pos += sizeof(BITMAPINFOHEADER);
		remained_size -= sizeof(BITMAPINFOHEADER);

		if( endianess::get_type_le( file_header->bfType ) != 0x4d42 )
			return;
		if(  endianess::get_type_le( bitmap_info_header->biBitCount ) <= 8 )
			return;
		sx[frame_count] = endianess::get_type_le( bitmap_info_header->biWidth );
		sy[frame_count] = endianess::get_type_le( bitmap_info_header->biHeight );
		if( frame_count == 0 )
		{
			bytes_per_pixel = endianess::get_type_le( bitmap_info_header->biBitCount )/ 8;
		}
		if( endianess::get_type_le( bitmap_info_header->biBitCount ) != bytes_per_pixel * 8 )
			return;
		int data_size = (sx[frame_count] * bytes_per_pixel + get_remained_bytes(sx[frame_count], bytes_per_pixel)) * sy[frame_count];
		if( remained_size < data_size)
			return;
		bitmap_data[frame_count] = current_pos;
		current_pos += data_size;
		remained_size -= data_size;
	}
}

DecompressorBmp::~DecompressorBmp()
{}

int DecompressorBmp::get_width(int frame)const
{
	return sx[frame];
}

int DecompressorBmp::get_height(int frame)const
{
	return sy[frame];
}

Format DecompressorBmp::get_best_format()const
{
	return bytes_per_pixel == 4 ? A8R8G8B8 : R8G8B8;
}

int DecompressorBmp::get_frame_count()const
{
	return frame_count;
}

void DecompressorBmp::reset(Format f, int frame)
{
	this->frame = frame;
	if( frame < 0 || frame >= frame_count )
		THROW(Gluck(__FUNCTION__, "frame does not exist"));
	desired_format = f;
	current_data = bitmap_data[frame];
///	adv_data = static_cast<const char *>( data );
///	adv_data += sizeof(BITMAPFILEHEADER);
///	adv_data += sizeof(BITMAPINFOHEADER);
}

void DecompressorBmp::unpack_scanline(void * dst)
{
///	if( mipmap_level % 2 == 0 )
///	{
		if( bytes_per_pixel == 3 )
			convert_scanline<R8G8B8>(desired_format, current_data, dst, get_width(frame) );
		else
			convert_scanline<A8R8G8B8>(desired_format, current_data, dst, get_width(frame) );
///	}
///	else
///	{
///		memset( dst, 0xFF, get_width(mipmap_level)*4 );
///	}
	current_data += bytes_per_pixel * get_width(frame);
	current_data += get_remained_bytes( get_width(frame), bytes_per_pixel );
}

void DecompressorBmp::skip_scanline(int count)
{
	current_data += count * bytes_per_pixel * get_width(frame);
	current_data += count * get_remained_bytes( get_width(frame), bytes_per_pixel );
}

} // namespace pictureformat
} // namespace liba

